from __future__ import with_statement
from threading import Lock

from pyfileutils import ls as _ls
from pyfileutils.LockFile import LockFile

__ls_cache = {}
__ls_cache_lock = Lock()

def ls(x):
    """Returns a list of LockFile instances.

    Normally, 'ls' will return a list of strings representing full
    paths to files found in 'x'.  This version returns LockFile
    instances opened on those strings.

    However, because the entire point is to ensure thread-safety,
    'ls' checks that it isn't returning duplicate LockFile objects,
    because that would allow threads to acquire totally separate Locks
    just by calling 'ls' again.  So 'ls' keeps a list of LockFile
    instances it's returned for a given 'x'.  If there are changes
    that list is updated.  Ultimately, that list is returned.

    """
    global __ls_cache
    global __ls_cache_lock
    with __ls_cache_lock:
        current_paths = _ls(x)
        current_lockfiles = [LockFile(y, overwrite=True) for y in current_paths]
        if x not in __ls_cache:
            __ls_cache[x] = current_lockfiles
            return current_lockfiles
        cached_lockfiles = __ls_cache[x]
        if __ls_cache[x] == current_paths:
            return __ls_cache[x]
        new = [y for y in current_lockfiles if y not in cached_lockfiles]
        dead = [y for y in cached_lockfiles if y not in current_lockfiles]
        __ls_cache[x] = [y for y in cached_lockfiles if y not in dead] + new
        return __ls_cache[x]

